import 'dart:math';
import 'dart:ui';

import 'package:flutter/material.dart';

class ZoomValue {
  // 当前缩放值
  final double scale;

  // 当前偏移值
  final Offset offset;

  ZoomValue({
    this.scale = 1.0,
    this.offset = Offset.zero,
  });

  ZoomValue copyWith({
    double scale,
    Offset offset,
  }) {
    return ZoomValue(
      scale: scale ?? this.scale,
      offset: offset ?? this.offset,
    );
  }

  static ZoomValue lerp(ZoomValue a, ZoomValue b, double t) {
    return ZoomValue(
      scale: lerpDouble(a.scale, b.scale, t),
      offset: Offset.lerp(a.offset, b.offset, t),
    );
  }

  @override
  bool operator ==(Object o) {
    if (identical(this, o)) return true;

    return o is ZoomValue && o.scale == scale && o.offset == offset;
  }

  @override
  int get hashCode => scale.hashCode ^ offset.hashCode;
}

class _ZoomValueTween extends Tween<ZoomValue> {
  _ZoomValueTween({ZoomValue begin, ZoomValue end})
      : super(begin: begin, end: end);

  @override
  ZoomValue lerp(double t) => ZoomValue.lerp(begin, end, t);
}

/// 可缩放/平移的盒子小部件
class GestureZoomBox extends StatefulWidget {
  final double maxScale;
  final double minScale;
  final Widget child;

  /// 通过最大缩放比例 [maxScale]、双击缩放比例 [doubleTapScale]、子部件 [child]、点击事件 [onPressed] 创建小部件
  GestureZoomBox({
    Key key,
    this.maxScale = 5.0,
    this.minScale = 1.0,
    @required this.child,
  })  : assert(maxScale >= 1.0),
        super(key: key) {
    assert(this.child.key is GlobalKey);
  }

  @override
  State<StatefulWidget> createState() {
    return _GestureZoomBoxState();
  }
}

class _GestureZoomBoxState extends State<GestureZoomBox>
    with TickerProviderStateMixin {
  // 上次缩放变化数据
  ScaleUpdateDetails _latestScaleUpdateDetails;

  ZoomValue _value = ZoomValue();

  @override
  Widget build(BuildContext context) {
    final Matrix4 transform = Matrix4.identity()
      ..translate(_value.offset.dx, _value.offset.dy)
      ..scale(_value.scale, _value.scale);

    Widget result = Transform(
      alignment: Alignment.center,
      transform: transform,
      child: widget.child,
    );

    result = Center(child: result);

    result = GestureDetector(
      behavior: HitTestBehavior.translucent,
      onScaleStart: _onScaleStart,
      onScaleUpdate: _onScaleUpdate,
      onScaleEnd: _onScaleEnd,
      child: result,
    );

    result = Listener(
      onPointerDown: _pointerDown,
      // onPointerCancel: (event) => _clearPonterSet(),
      onPointerUp: _pointerUp,
      child: result,
    );

    return result;
  }

  AnimationController _valueAnimationController;
  Animation<ZoomValue> _valueAnimation;

  @override
  void initState() {
    super.initState();
  }

  @override
  void dispose() {
    _valueAnimationController?.dispose();
    super.dispose();
  }

  bool get _shouldIngnoreZoom => _pointerSet.length != 2;
  bool _ignoreZoom = false;

  final _pointerSet = Set();
  void _pointerDown(PointerDownEvent event) {
    _pointerSet.add(event.pointer);
    _print('按下 pointer: ${event.pointer}');
  }

  void _pointerUp(PointerUpEvent event) {
    _pointerSet.remove(event.pointer);
    _print('抬起 pointer: ${event.pointer}');
    if (_shouldIngnoreZoom) {
      _boundceBack();
    }
  }

  void _onScaleStart(ScaleStartDetails details) {
    _print('开始触摸，$_pointerSet');
    _latestScaleUpdateDetails = null;

    _ignoreZoom = _shouldIngnoreZoom;
  }

  /// 处理缩放变化 [details]
  void _onScaleUpdate(ScaleUpdateDetails details) {
    if (_ignoreZoom) {
      // _boundceBack();
      return;
    }

    if (_latestScaleUpdateDetails == null) {
      _latestScaleUpdateDetails = details;
      return;
    }

    _scaling(details);
    _dragging(details);

    _latestScaleUpdateDetails = details;

    setState(() {});
    _print('更新');
  }

  /// 执行缩放
  void _scaling(ScaleUpdateDetails details) {
    double _scale = _value.scale;
    Offset _offset = _value.offset;

    // 计算缩放比例
    double deltaScale = details.scale - _latestScaleUpdateDetails.scale;
    if (details.scale < 1.0 && _scale > 1.0) {
      deltaScale *= _scale;
    }
    if (_scale < 1.0 && deltaScale < 0) {
      deltaScale *= (_scale - 0.5);
    } else if (_scale > widget.maxScale && deltaScale > 0) {
      deltaScale *= (2.0 - (_scale - widget.maxScale));
    }
    _scale = max(_scale + deltaScale, 0.0);

    // 计算缩放后偏移前（缩放前后的内容中心对齐）的左上角坐标变化
    double scaleOffsetX = context.size.width * (_scale - 1.0) / 2;
    double scaleOffsetY = context.size.height * (_scale - 1.0) / 2;
    // 将缩放前的触摸点映射到缩放后的内容上
    double scalePointDX =
        (details.localFocalPoint.dx + scaleOffsetX - _offset.dx) / _scale;
    double scalePointDY =
        (details.localFocalPoint.dy + scaleOffsetY - _offset.dy) / _scale;
    // 计算偏移，使缩放中心在屏幕上的位置保持不变
    _offset += Offset(
      (context.size.width / 2 - scalePointDX) * deltaScale,
      (context.size.height / 2 - scalePointDY) * deltaScale,
    );

    _value = ZoomValue(scale: _scale, offset: _offset);
  }

  /// 执行拖动
  void _dragging(ScaleUpdateDetails details) {
    Offset _offset = _value.offset;
    Offset deltaOffset =
        (details.localFocalPoint - _latestScaleUpdateDetails.localFocalPoint);
    _offset += deltaOffset;

    _value = _value.copyWith(offset: _offset);
  }

  /// 缩放/拖动结束
  void _onScaleEnd(ScaleEndDetails details) {
    if (_shouldIngnoreZoom) return;
  }

  void _boundceBack() {
    // if (_latestScaleUpdateDetails == null) return;

    final childKey = widget.child.key as GlobalKey;
    final childSize = childKey.currentContext.size;
    final outerSize = context.size;

    double _scale = _value.scale;
    Offset _offset = _value.offset;

    _scale = _scale.clamp(widget.minScale, widget.maxScale);

    final double computedWidth = childSize.width * _scale;
    final double computedHeight = childSize.height * _scale;

    final double screenWidth = outerSize.width;
    final double screenHeight = outerSize.height;

    // _print(
    //   'position: $_position, outSize: $outerSize, childSize: ($computedWidth, $computedHeight)',
    // );

    double _dx = _offset.dx, _dy = _offset.dy;
    if (computedWidth < screenWidth) {
      _dx = 0;
    } else {
      double deltaX = (screenWidth - computedWidth) / 2;
      deltaX = deltaX.abs();
      _dx = _dx.clamp(-deltaX, deltaX);
    }

    if (computedHeight < screenHeight) {
      _dy = 0;
    } else {
      double deltaY = (screenHeight - computedHeight) / 2;
      deltaY = deltaY.abs();
      _dy = _dy.clamp(-deltaY, deltaY);
    }
    _offset = Offset(_dx, _dy);

    final newValue = ZoomValue(offset: _offset, scale: _scale);

    // 动画更新
    _animate(_value, newValue);

    // 直接更新
    // _value = newValue;
    // setState(() {});

    _latestScaleUpdateDetails = null;
    _print('松手');
  }

  Future _animate(ZoomValue from, ZoomValue to) async {
    if (from == to) return;

    _print('开始动画');
    // _valueAnimationController.stop();
    // _valueAnimationController..value = 0.0;
    // _valueAnimationController.reset();

    _valueAnimationController?.dispose();
    _valueAnimationController = AnimationController(vsync: this)
      ..addListener(() {
        if (_valueAnimation == null) return;
        ZoomValue value = _valueAnimation.value;
        this._value = value;
        setState(() {});
      })
      ..addStatusListener((status) {
        _print(status);
      });
    _valueAnimation =
        _ZoomValueTween(begin: from, end: to).animate(CurvedAnimation(
      curve: Curves.decelerate,
      parent: _valueAnimationController,
    ));

    await _valueAnimationController.fling(velocity: 0.6);
  }
}

void _print(Object content) {
  final time = DateTime.now().toIso8601String();
  print('$time $content');
}
